package org.knight.gearbox.script;

import org.mvel2.CompileException;
import org.mvel2.MVEL;
import org.mvel2.ParserContext;
import org.mvel2.compiler.CompiledExpression;
import org.mvel2.compiler.ExpressionCompiler;

import java.util.*;

public class MVELContainer implements Container {

    private Map<String, MVELExecution> executeExpressions = new HashMap<>();

    public void execute(String name, Map vars) {
        MVELExecution mvelExecution = executeExpressions.get(name);
        if(mvelExecution == null) {
            throw new RuntimeException("no corresponding scripts "+name);
        }
        mvelExecution.execute(vars);
    }


    public boolean compile(String name, String raw) {
        MVELExecution execution = new MVELExecution();
        Map result = normalizeScript(raw);
        execution.name = name;
        execution.imports = (List) result.get("imports");
        execution.scripts = (String) result.get("scripts");
        execution.raw = raw;
        execution.compile();
        executeExpressions.put(name, execution);
        return true;
    }

    private Map normalizeScript(String raw) {
        List<String> imports = new ArrayList<>();
        if (raw.indexOf("import ") > -1) {//has import clauses

            int i = raw.indexOf("import ");
            int j = raw.indexOf("\r\n", i);
            String importClause = raw.substring(i, j);
            imports.add(importClause);
            raw = raw.substring(j);
        }
        Map result = new HashMap();

        result.put("imports", imports);
        result.put("scripts", raw);
        return result;
    }

    private static String stripePackageName(String importClause) {
        return importClause.replaceAll("import ","").replaceAll(" ","")
                .replaceAll(".*","");
    }


    private static class MVELExecution {
        ExpressionCompiler compiler;

        String raw;
        String scripts;
        List<String> imports;
        String name;
        CompiledExpression expression;


        void compile() throws CompileException{

            ParserContext context = new ParserContext();
            imports.stream()
                    .forEach(importClause -> {
                        context.addPackageImport(stripePackageName(importClause));
                    });

            compiler = new ExpressionCompiler(scripts, context);
            expression = compiler.compile();
        }

        void execute(Map vars) {
            MVEL.executeExpression(expression, vars);
        }
    }


}
